package org.ns.framework.webmvc.error;

import org.ns.framework.webmvc.ResponseEntity;
import org.ns.framework.webmvc.ResponseEntityBuild;
import org.ns.framework.webmvc.ResponseStatus;
import org.ns.framework.webmvc.anns.ApiController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;

@Order(Integer.MIN_VALUE)
public class ExceptionHandler implements HandlerExceptionResolver, ApplicationContextAware {
	
	private static final Logger log = LoggerFactory.getLogger(ExceptionHandler.class);

	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		if (null != handler && handler instanceof HandlerMethod) {
			if (!((HandlerMethod) handler).getBeanType().isAnnotationPresent(ApiController.class)) {
				return null;
			}
		}
        try {
            ResponseEntity responseEntity;
            ResponseEntityBuild responseEntityBuild;
            if (ex instanceof ApiException) { // api 异常
                // API 异常
            	responseEntityBuild = ((ApiException) ex).create();
            } else if (ex instanceof MethodArgumentNotValidException) { // @Validated验证
            	MethodArgumentNotValidException m = (MethodArgumentNotValidException) ex;
    			ObjectError error = m.getBindingResult().getAllErrors().get(0);
    			String msg = error.getDefaultMessage();
    			String errplace = error.getObjectName();
    			responseEntityBuild = ResponseStatus.P_VALUE_ERROR
	            	.create()
	            	.setMessage(msg)
	            	.setErrplace(errplace)
	            	;
            } else if (ex instanceof ConstraintViolationException) { // 方法参数使用@Validated验证 参数不正确
            	ConstraintViolationException cve = (ConstraintViolationException) ex;
            	ConstraintViolation<?> cv = cve.getConstraintViolations().iterator().next();
            	responseEntityBuild = ResponseStatus.P_VALUE_ERROR.apiEx()
            	.setMessage(cv.getMessage())
            	.create()
            	;
            }
            else if (ex instanceof MissingPathVariableException) {
                MissingPathVariableException mex = (MissingPathVariableException) ex;
                responseEntityBuild = ResponseStatus.P_NOTNULL.create()
                        .setErrplace(mex.getVariableName());
            } else if (ex instanceof MissingServletRequestParameterException) {
                // 参数必填的异常
                MissingServletRequestParameterException mex = (MissingServletRequestParameterException) ex;
                responseEntityBuild = ResponseStatus.P_NOTNULL.create()
                        .setErrplace(mex.getParameterName());
            } else if (ex instanceof NumberFormatException) {
                // 参数值错误 - 数字格式转化失败
            	responseEntityBuild = ResponseStatus.P_VALUE_ERROR.create();
            } else if (ex instanceof MethodArgumentTypeMismatchException) {
                // 参数值错误 - 类型转化失败
                MethodArgumentTypeMismatchException mex = (MethodArgumentTypeMismatchException) ex;
                responseEntityBuild = ResponseStatus.P_VALUE_ERROR.create()
                        .setErrplace(mex.getName());
            } 
//            else if (ex instanceof org.springframework.web.servlet.resource.NoResourceFoundException) {
//            	org.springframework.web.servlet.resource.NoResourceFoundException
//            	ne = (org.springframework.web.servlet.resource.NoResourceFoundException)ex;
//            	responseEntityBuild = ResponseStatus.NOT_FOUND.create()
//            			.setErrplace(ne.getResourcePath());
//            } 
            else { // 其他异常

                if (ex instanceof IllegalStateException) {
                	responseEntityBuild = ResponseStatus.ILLEGAL_STATE.create();
                } else {
                	responseEntityBuild = ResponseStatus.SYSTEM_ERROR.create();
                }
                if (log.isErrorEnabled()) {
                	try {
                		log.error("err-uri: " + request.getRequestURI(), ex);
                	} catch (Exception e) {
                		log.error("err-uri: null", ex);
					}
                } else {
                	ex.printStackTrace();
                }
            }
            
            responseEntityBuild.setApplicationContext(applicationContext);
            responseEntity = responseEntityBuild.build(request);
            responseEntity.out(response);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ModelAndView();
    }
}
